﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace Extented.UI.Core.Native
{
    public class ValueChangedStorage
    {
        #region innerClasses
        struct ValueChangedStorageKey
        {
            readonly WeakReference targetReference;
            readonly DependencyPropertyDescriptor descriptor;
            readonly int hCode;
            public object Target { get { return targetReference.With(x => x.Target); } }
            public DependencyPropertyDescriptor Descriptor { get { return descriptor; } }
            public ValueChangedStorageKey(object target, DependencyPropertyDescriptor descriptor)
            {
                targetReference = new WeakReference(target);
                this.descriptor = descriptor;
                hCode = (target.GetHashCode() * 397) ^ descriptor.GetHashCode();
            }
            public override bool Equals(object obj)
            {
                return obj.GetHashCode() == hCode;
            }
            public override int GetHashCode()
            {
                return hCode;
            }
        }
        class ValueChangedStorageRecord : IDisposable
        {
            readonly object target;
            readonly DependencyPropertyDescriptor descriptor;
            readonly EventHandler handler;
            readonly List<RenderPropertyChangedListenerContext> contexts = new List<RenderPropertyChangedListenerContext>();
            public bool IsActive { get { return contexts.Count > 0; } }
            public ValueChangedStorageRecord(object target, DependencyPropertyDescriptor descriptor)
            {
                this.target = target;
                this.descriptor = descriptor;
                handler = new EventHandler(OnValueChanged);
                descriptor.AddValueChanged(target, handler);
            }
            void OnValueChanged(object sender, EventArgs args)
            {
                var invocationList = contexts.ToList();
                foreach (var context in invocationList)
                {
                    context.PreviewValueChanged(sender, args);
                }
                foreach (var context in invocationList)
                {
                    context.ValueChanged(sender, args);
                }
            }
            public void AddListener(RenderPropertyChangedListenerContext context)
            {
                contexts.Add(context);
                OnValueChanged(target, EventArgs.Empty);
            }
            public void RemoveListener(RenderPropertyChangedListenerContext context)
            {
                contexts.Remove(context);
            }
            public void Dispose()
            {
                contexts.Clear();
                descriptor.RemoveValueChanged(target, handler);
            }
        }
        #endregion
        readonly Dictionary<ValueChangedStorageKey, ValueChangedStorageRecord> handlers;
        public ValueChangedStorage()
        {
            handlers = new Dictionary<ValueChangedStorageKey, ValueChangedStorageRecord>();
        }
        public void SubscribeValueChanged(object target, RenderPropertyChangedListenerContext context)
        {
            var descriptor = context.Descriptor;
            if (descriptor == null)
                return;
            var key = new ValueChangedStorageKey(target, descriptor);
            ValueChangedStorageRecord record = null;
            if (!handlers.TryGetValue(key, out record))
            {
                record = new ValueChangedStorageRecord(target, descriptor);
                handlers[key] = record;
            }
            record.AddListener(context);
        }
        public void UnsubscribeValueChanged(object target, RenderPropertyChangedListenerContext context)
        {
            DependencyPropertyDescriptor descriptor = context.Descriptor;
            if (descriptor == null)
                return;
            var key = new ValueChangedStorageKey(target, descriptor);
            ValueChangedStorageRecord record = null;
            if (handlers.TryGetValue(key, out record))
            {
                record.RemoveListener(context);
                if (!record.IsActive)
                {
                    record.Dispose();
                    handlers.Remove(key);
                }
            }
        }
    }
}
